//
//  MusicPlayerManager.swift
//  音乐播放管理器，封装了常用功能，播放，暂停等功能
//  目的是对外只提供必要的接口
//  内部可以自由重构，换其他框架
//
//  Created by smile on 2019/4/14.
//  Copyright © 2019 ixuea. All rights reserved.
//

import Foundation

//导入系统媒体框架
import MediaPlayer

class MusicPlayerManager:NSObject {
     public static let STATUS="status"
    public static let LOADED_TIME_RANGES="loadedTimeRanges"
    
    static var instance:MusicPlayerManager?
    
    /// 代理
    var delegate:MusicPlayerDelegate? {
        didSet {
            
            if let _ = self.delegate {
                //有回调函数就执行
                //启动进度更新
                if self.isPlaying() {
                    //正在播放
                    //启动进度通知
                    startPublishProgress()
                }
            }else{
                //没有回调函数就停止
                stopPublishProgress()
            }
            
        }
    }
    
    /// 播放完成回调
    var onComplete:((_ data:Song) -> Void)?
   
    /// 播放器
    var player:AVPlayer!
   
    /// 正在播放的音乐
    var data:Song?

    /// 进度更新返回的对象
    /// 取消进度更新要用到它
    var playTimeObserver:Any?
    
    /// 播放状态
    var status:PlayStatus = .none
    
    // 单例设计模式
    static func shared() -> MusicPlayerManager {
        if instance == nil {
            instance = MusicPlayerManager()
        }
        
        return instance!
    }
    
    /// 初始化
    override init() {
        super.init()
        player=AVPlayer.init()
    }
    
    func initListeners() {
        //KVO方式监听播放状态
        //KVC:Key-Value Coding,另一种获取对象字段的值，类似字典
        //KVO:Key-Value Observing,建立在KVC基础上，能够观察一个字段值的改变
        player.currentItem?.addObserver(self, forKeyPath: "status", options: .new, context: nil)
        
        //播放结束事件
//        NotificationCenter.default.addObserver(self, forKeyPath: <#T##String#>, options: NSKeyValueObservingOptions, context: <#T##UnsafeMutableRawPointer?#>)
        NotificationCenter.default.addObserver(self, selector: #selector(onComplete(notification:)), name: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: player.currentItem)
    }
    
    /// KVO方式监听回调
    ///
    /// - Parameters:
    ///   - keyPath: <#keyPath description#>
    ///   - object: <#object description#>
    ///   - change: <#change description#>
    ///   - context: <#context description#>
    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
        print("MusicPlayerManager observeValue:\(keyPath)")
        
        //判断监听的字段
        //因为可以监听多个字段
        if MusicPlayerManager.STATUS==keyPath {
            //状态
            switch player.status {
            case .readyToPlay:
                //准备播放
                self.data!.duration=Float(CMTimeGetSeconds(player.currentItem!.asset.duration))
                print("MusicPlayerManager duration:\(self.data!.duration)")
        
                //回调代理
                delegate?.onPrepared(data!)
                
                //更新系统媒体中心信息
                updateMediaInfo()
                break
            case .failed:
                //播放失败
                print("MusicPlayerManager failed:\(player.error)")
                status = .error
                break
            default:
                //未知转态
                print("MusicPlayerManager unknown")
                break
            }
        }
        
    }
    
    /// 移除监听器
    func removeListeners() {
        player.currentItem?.removeObserver(self, forKeyPath: "status")
        
        NotificationCenter.default.removeObserver(self)
    }
    
    /// 播放完成回调方法
    ///
    /// - Parameter notification: <#notification description#>
    @objc func onComplete(notification:Notification) {
        onComplete?(data!)
    }
    
    /// 播放
    ///
    /// - Parameters:
    ///   - uri: <#uri description#>
    ///   - song: <#song description#>
    func play(_ uri:String,_ song:Song) {
        status = .playing
        
        self.data=song
        
        var url:URL!
        if uri.hasPrefix("http") {
            //网络地址
            url=URL(string: uri)
        } else {
            //本地地址
            url=URL(fileURLWithPath: uri)
        }
    
        /// 创建一个播放Item
        let playerItem = AVPlayerItem(url: url)
        
        //替换到原来的
        player.replaceCurrentItem(with: playerItem)
        
        //播放
        player.play()
        
        //设置监听器
        //因为监听器是针对Item
        //所以播放新的Item要重新设置
        initListeners()
        
        //回调代理
        delegate?.onPlaying(data!)
        
        //启动进度通知
        startPublishProgress()
        
        //歌词处理
        //真实项目可能会
        //将歌词这个部分拆分到其他组件中
        if song.lyric == nil {
            //没有歌词才请求
            //真实项目中可以会缓存歌词
            
            //获取歌词数据
            Api.shared
                .songDetail(id: song.id)
                .subscribeOnSuccess { data in
                    if let data = data?.data{
                        self.data!.lyric=data.lyric
                        self.data!.style=data.style
                        
                        // 这里只是通知歌词数据改变了
                        // 但不一定就有歌词
                        self.delegate?.onLyricChanged(self.data!)
                    }
            }
        }
        
    }
    
    
    /// 是否在播放
    ///
    /// - Returns: <#return value description#>
    func isPlaying() -> Bool {
        return status == .playing
    }

    /// 暂停
    func pause() {
        status = .pause
        
        //移除监听器
        removeListeners()
        
        //播放器暂停
        player.pause()
        
        //回调代理
        delegate?.onPaused(data!)
        
        //通知通知进度
        stopPublishProgress()
    }

    /// 继续播放
    ///
    /// - Returns: <#return value description#>
    func resume() {
        status = .playing
        
        //设置监听器
        initListeners()
        
        //播放
        player.play()
        
        delegate?.onPlaying(data!)
        
        startPublishProgress()
    }

    /// 移动到指定位置播放
    ///
    /// - Parameter progress: <#progress description#>
    func seekTo(_ progress:Float) {
        let positionTime=CMTime(seconds: Double(progress), preferredTimescale: 1)
        
        player.seek(to: positionTime)

    }

    /// 是否单曲循环
    ///
    /// - Parameter looping: <#looping description#>
    func setLooping(_ looping:Bool) {

    }

    /// 销毁
    func destroy() {

    }
    
    /// 启动播放进度通知
    func startPublishProgress() {
        if let _ = playTimeObserver {
            //已经启动了
            return
        }
        
        //1/60秒
        //就是16毫秒
        playTimeObserver=player.addPeriodicTimeObserver(forInterval: CMTime(value: CMTimeValue(1.0), timescale: 60), queue: DispatchQueue.main) { time in
            //当前播放的时间
            self.data!.progress=Float(CMTimeGetSeconds(time))

            guard let delegate = self.delegate else {
                //如果没有了回调函数就停止定时器
                self.stopPublishProgress()
                return
            }

            //有回调函数就执行
            delegate.onProgress(data: self.data!, progress: self.data!.progress, duration: self.data!.duration)
            
        }
    }

    /// 更新系统媒体控制中心的信息
    /// 不需要更新进度到控制中心
    /// 他那边会自动倒计时
    func updateMediaInfo() {
        //下载图片
        //这部分可以封装
        //因为其他界面可能也会用
        let manager=SDWebImageManager.shared()
        
        let imageUrl=ResourceUtil.resourceUri(data!.banner)
        
        manager.imageDownloader!.downloadImage(with: URL(string: imageUrl), options: SDWebImageDownloaderOptions.progressiveDownload, progress: { (receivedSize, expectedSize, targetURL) in
            //下载进度
            //这里用不到
        }) { (image, data, error, finished) in
            if let image=image{
                //图片下载完成
                self.setMediaInfo(image)
            }
        }
    }
    
    /// 设置系统媒体中心信息
    ///
    /// - Parameter image: <#image description#>
    func setMediaInfo(_ image:UIImage) {
        //初始化一个可变字典
        var songInfo:[String:Any]=[:]
        
        //初始化一个封面
        //要导入MediaPlayer
        let albumArt=MPMediaItemArtwork(boundsSize: CGSize(width: 100, height: 100)) { (size) -> UIImage in
            return image
        }
        
        //设置封面
        songInfo[MPMediaItemPropertyArtwork]=albumArt
        
        //歌曲名称
        songInfo[MPMediaItemPropertyTitle]=data!.title
        
        //歌手
        songInfo[MPMediaItemPropertyArtist]=data!.singer.nickname
        
        //专辑名
        //服务器没返回这样的数据
        songInfo[MPMediaItemPropertyAlbumTitle]="这是专辑名称"
        
        //流派
        songInfo[MPMediaItemPropertyGenre]="这是流派"
        
        //设置总时长
        songInfo[MPMediaItemPropertyPlaybackDuration]=data!.duration
        
        //设置已经播放时长
        //不用设置已经播放的时长
        //他会自动计算
//        songInfo[MPNowPlayingInfoPropertyElapsedPlaybackTime]=20
        
        //设置歌词
        songInfo[MPMediaItemPropertyLyrics]="这是歌词"
        
        //设置到系统
        MPNowPlayingInfoCenter.default().nowPlayingInfo=songInfo
    }
    
    /// 停止进度通知
    func stopPublishProgress() {
        if let playTimeObserver = playTimeObserver {
            player.removeTimeObserver(playTimeObserver)
            self.playTimeObserver=nil
        }
        
    }
    
    /// 保存当前这首音乐到数据库
    func saveCurrentSong() {
        ORMUtil.shared().saveSong(data!, PreferenceUtil.userId()!)
        
        //设置最后播放音乐的Id
        PreferenceUtil.setLastPlaySongId(data!.id)
    }
   
}

/// 播放器状态
///
/// - none: 没有初始化
/// - playing: 播放中
/// - pause: 暂停中
/// - error: 播放失败
enum PlayStatus {
    case none
    case playing
    case pause
    case error
}

/// 播放器代理
protocol MusicPlayerDelegate: NSObjectProtocol {
    
    /// <#Description#>
    ///
    /// - Parameters:
    ///   - data: <#data description#>
    ///   - progress: <#progress description#>
    ///   - duration: <#duration description#>
    func onProgress(data:Song,progress:Float,duration:Float)
    
    /// 暂停了播放
    ///
    /// - Parameter data: <#data description#>
    func onPaused(_ data:Song)
    
    /// 正在播放
    ///
    /// - Parameter data: <#data description#>
    func onPlaying(_ data:Song)
    
    /// 播放器准备完毕
    /// 可以获取到音乐时长
    ///
    /// - Parameter data: <#data description#>
    func onPrepared(_ data:Song)
    
    /// 歌词信息改变了
    ///
    /// - Parameter data: <#data description#>
    func onLyricChanged(_ data:Song)
    
    /// 播放发生了错误
    ///
    /// - Parameter data: <#data description#>
    func onError(_ data:Song)
}
